package com.along.textdemo.manager;

import android.media.AudioFormat;
import android.media.AudioRecord;
import android.media.MediaRecorder;
import android.os.Handler;
import android.os.Message;
import android.os.SystemClock;
import android.util.Log;

import com.along.textdemo.utils.FileUtils;
import com.along.textdemo.utils.PcmToWavUtil;

import java.io.DataOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;

/**
 * 使用AudioRecord，录音并获取音量大小
 */
public class AudioRecordManager {
    public static final String TAG = "AudioRecordManager";

    public final static int VOICE_VOLUME = 0x1111;
    private AudioRecord mRecorder;
    private DataOutputStream dos;
    private Thread recordThread;
    private boolean isStart = false;
    private static AudioRecordManager mInstance;
    private  int bufferSize;
    private double volume = 0;
    private String outPath;//音频转换后输出位置

    public AudioRecordManager() {
        bufferSize = AudioRecord.getMinBufferSize(
                8000,
                AudioFormat.CHANNEL_IN_MONO,
                AudioFormat.ENCODING_PCM_16BIT);
        mRecorder = new AudioRecord(
                // 音频获取源
                MediaRecorder.AudioSource.MIC,
                8000,
                // 设置音频的录制的声道CHANNEL_IN_STEREO为双声道，CHANNEL_CONFIGURATION_MONO为单声道
                AudioFormat.CHANNEL_IN_MONO,
                // 音频数据格式:PCM 16位每个样本。保证设备支持。PCM 8位每个样本。不一定能得到设备支持。
                AudioFormat.ENCODING_PCM_16BIT,
                // 缓冲区字节大小
                bufferSize);
    }

    /**
     * 获取单例引用
     *
     * @return
     */
    public static AudioRecordManager getInstance() {
        if (mInstance == null) {
            synchronized (AudioRecordManager.class) {
                if (mInstance == null) {
                    mInstance = new AudioRecordManager();
                }
            }
        }
        return mInstance;
    }

    /**
     * 销毁线程方法
     */
    private void destroyThread() {
        try {
            isStart = false;
            if (null != recordThread && Thread.State.RUNNABLE == recordThread.getState()) {
                try {
                    Thread.sleep(500);
                    recordThread.interrupt();
                } catch (Exception e) {
                    recordThread = null;
                }
            }
            recordThread = null;
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            recordThread = null;
        }
    }

    /**
     * 启动录音线程
     */
    int totalSize;
    private void startThread(Handler handler) {
        destroyThread();
        isStart = true;
        totalSize = 0;
        if (recordThread == null) {
            recordThread = new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        //设置线程优先级，android.os.Process.THREAD_PRIORITY_AUDIO-标准音乐播放使用的线程优先级
                        android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_URGENT_AUDIO);
                        int bytesRecord;
                        //int bufferSize = 320;
                        byte[] tempBuffer = new byte[bufferSize];
                        if (mRecorder.getState() != AudioRecord.STATE_INITIALIZED) {
                            stopRecord();
                            return;
                        }
                        mRecorder.startRecording();
                        //文件头 跟初始化设置对应
                        writeHeader(dos,8000,16,1);
                        while (isStart) {
                            if (null != mRecorder) {
                                //bytesRecord是实际读取的数据长度，一般而言bytesRecord会小于buffersize
                                bytesRecord = mRecorder.read(tempBuffer, 0, bufferSize);
                                if (bytesRecord == AudioRecord.ERROR_INVALID_OPERATION || bytesRecord == AudioRecord.ERROR_BAD_VALUE) {
                                    continue;
                                }
                                if (bytesRecord != 0 && bytesRecord != -1) {
                                    long v = 0;
                                    // 将 buffer 内容取出，进行平方和运算
                                    for (short value : tempBuffer) {
                                        v += value * value;
                                    }
                                    // 平方和除以数据总长度，得到音量大小。
                                    double mean = v / (double) bytesRecord;
                                    volume = 10 * Math.log10(mean);
                                    Log.d(TAG, "音量的值 = " + volume + "dB");

                                    Message message = new Message();
                                    message.what = VOICE_VOLUME;
                                    message.arg1 = (int) (volume);
                                    handler.sendMessage(message);


                                    //在此可以对录制音频的数据进行二次处理 比如变声，压缩，降噪，增益等操作
                                    //我们这里直接将pcm音频原数据写入文件 这里可以直接发送至服务器 对方采用AudioTrack进行播放原数据
                                    dos.write(tempBuffer, 0, bytesRecord);
                                    totalSize += bytesRecord;
                                    SystemClock.sleep(100);// 一秒十次
                                } else {
                                    break;
                                }
                            }
                        }
                        //录音结束  这里不用关闭，stop的时候关了，严谨些还是关闭吧
                        if (dos != null) {
                            //pcm2wav 格式转换
                            writeDataSize(dos,outPath,totalSize);
                            dos.close();// 关闭写入流
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }

            });
            recordThread.start();
        }
    }


    /**
     * 保存文件
     * @param path
     * @throws Exception
     */
    private void setPath(String path) throws Exception {
        File file = new File(path);
        if (!file.exists()) {
            file.mkdirs();
        }
        dos = new DataOutputStream(new FileOutputStream(file, true));
    }

    /**
     * 启动录音
     * handler 是后添加的，方便从线程中去数据
     * path = FileUtils.createAudioPath("pcm");
     *  outPath = FileUtils.createAudioPath("wav");
     *
     * @param path
     */

    public void startRecord(String path,Handler handler) {
        try {
            path = FileUtils.createAudioPath("pcm");
            outPath = FileUtils.createAudioPath("wav");
            setPath(path);
            startThread(handler);
        } catch (Exception e) {
            Log.d(TAG, "Exception =--- " + e);
            e.printStackTrace();
        }
    }

    /**
     * 停止录音
     */
    public void stopRecord() {
        try {
            destroyThread();
            if (mRecorder != null) {
                if (mRecorder.getState() == AudioRecord.STATE_INITIALIZED) {
                    mRecorder.stop();
                }
                if (mRecorder != null) {
                    mRecorder.release();
                }
            }
            if (dos != null) {
                dos.flush();
                dos.close();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 获取音量,可以直接获取音量，也可以通过handle的方式，发消息获取音量
     * @return
     */
    public int getVolume(){
        if (isStart){
            return (int)volume;
        }
        return 0;
    }



    private static void writeHeader(DataOutputStream dataOutputStream, int sampleRateInHz, int bitsPerSample, int channels) {
        if (dataOutputStream == null) {
            return;
        }
        WavFileHeader header = new WavFileHeader(sampleRateInHz, bitsPerSample, channels);
        try {
            dataOutputStream.writeBytes(header.mChunkID);
            //关闭文件时需要追加设置这里的大小
            dataOutputStream.write(intToByteArray((int) header.mChunkSize), 0, 4);
            dataOutputStream.writeBytes(header.mFormat);
            dataOutputStream.writeBytes(header.mSubChunk1ID);
            dataOutputStream.write(intToByteArray((int) header.mSubChunk1Size), 0, 4);
            dataOutputStream.write(shortToByteArray((short) header.mAudioFormat), 0, 2);
            dataOutputStream.write(shortToByteArray((short) header.mNumChannel), 0, 2);
            dataOutputStream.write(intToByteArray((int) header.mSampleRate), 0, 4);
            dataOutputStream.write(intToByteArray((int) header.mByteRate), 0, 4);
            dataOutputStream.write(shortToByteArray((short) header.mBlockAlign), 0, 2);
            dataOutputStream.write(shortToByteArray((short) header.mBitsPerSample), 0, 2);
            dataOutputStream.writeBytes(header.mSubChunk2ID);
            dataOutputStream.write(intToByteArray((int) header.mSubChunk2Size), 0, 4);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }



    /**
     * wav文件的头信息
     */
    private static class WavFileHeader {
        public static final int WAV_FILE_HEADER_SIZE = 44;
        public static final int WAV_CHUNKSIZE_EXCLUDE_DATA = 36;
        public static final int WAV_CHUNKSIZE_OFFSET = 4;
        public static final int WAV_SUB_CHUNKSIZE1_OFFSET = 16;
        public static final int WAV_SUB_CHUNKSIZE2_OFFSET = 40;
        public String mChunkID = "RIFF";
        public int mChunkSize = 0;
        public String mFormat = "WAVE";
        public String mSubChunk1ID = "fmt ";
        public int mSubChunk1Size = 16;
        public short mAudioFormat = 1;
        public short mNumChannel = 1;
        public int mSampleRate = 8000;
        public int mByteRate = 0;
        public short mBlockAlign = 0;
        public short mBitsPerSample = 8;

        public String mSubChunk2ID = "data";
        public int mSubChunk2Size = 0;

        public WavFileHeader(int sampleRateInHz, int bitsPerSample, int channels) {
            mSampleRate = sampleRateInHz;
            mBitsPerSample = (short) bitsPerSample;
            mNumChannel = (short) channels;
            mByteRate = mSampleRate * mNumChannel * mBitsPerSample / 8;
            mBlockAlign = (short) (mNumChannel * mBitsPerSample / 8);
        }
    }

    //基本数据类型转为byte数组
    private static byte[] intToByteArray(int data) {
        return ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN).putInt(data).array();
    }

    private static byte[] shortToByteArray(short data) {
        return ByteBuffer.allocate(2).order(ByteOrder.LITTLE_ENDIAN).putShort(data).array();
    }

    private static void writeDataSize(DataOutputStream outputStream, String filePath, long totalSize) {

        if (outputStream == null) {
            return;
        }
        try {
            RandomAccessFile wavFile = new RandomAccessFile(filePath, "rw");
            wavFile.seek(WAV_CHUNKSIZE_OFFSET);
            wavFile.write(intToByteArray((int) (totalSize + WavFileHeader.WAV_CHUNKSIZE_EXCLUDE_DATA)), 0, 4);
            wavFile.seek(WAV_SUB_CHUNKSIZE2_OFFSET);
            wavFile.write(intToByteArray((int) (totalSize)), 0, 4);
            wavFile.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
            return;
        } catch (IOException e) {
            e.printStackTrace();
            return;
        }
        return;
    }

    public static final int WAV_CHUNKSIZE_OFFSET = 4;
    public static final int WAV_SUB_CHUNKSIZE2_OFFSET = 40;
}